package server

import (
	"context"
	"encoding/json"
	"github.com/gorilla/mux"
	"net/http"
	"simple-http-server/server/middleware"
	"simple-http-server/store"
	"time"
)

type BookServer struct {
	s   store.BookRepository
	srv *http.Server
}

func (bs *BookServer) createBookHandler(w http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(req.Body)
	var book store.Book
	if err := decoder.Decode(&book); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if err := bs.s.Create(&book); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
}

func (bs *BookServer) updateBookHandler(w http.ResponseWriter, r *http.Request) {
	decoder := json.NewDecoder(r.Body)
	var book store.Book
	if err := decoder.Decode(&book); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if err := bs.s.Update(&book); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

}

func (bs *BookServer) deleteBookHandler(w http.ResponseWriter, r *http.Request) {
	name, ok := mux.Vars(r)["name"]
	if !ok {
		http.Error(w, "no param name ", http.StatusBadRequest)
		return
	}
	if err := bs.s.Delete(name); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
}

func (bs *BookServer) queryByName(w http.ResponseWriter, r *http.Request) {
	name, ok := mux.Vars(r)["name"]
	if !ok {
		http.Error(w, "no param name ", http.StatusBadRequest)
		return
	}
	book, err := bs.s.Get(name)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	response(w, book)
}

func (bs *BookServer) queryAll(w http.ResponseWriter, r *http.Request) {
	all, err := bs.s.GetAll()
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	response(w, all)
}

func response(w http.ResponseWriter, v interface{}) {
	data, err := json.Marshal(v)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	w.Header().Set("Content-Type", "application/json")
	w.Write(data)
}

func NewBookServer(addr string, s store.BookRepository) *BookServer {
	srv := &BookServer{
		s:   s,
		srv: &http.Server{Addr: addr},
	}
	router := mux.NewRouter()
	router.HandleFunc("/book", srv.createBookHandler).Methods("POST")
	router.HandleFunc("/book/{id}", srv.updateBookHandler).Methods("POST")
	router.HandleFunc("/book/{id}", srv.queryByName).Methods("GET")
	router.HandleFunc("/book", srv.queryAll).Methods("GET")
	router.HandleFunc("/book/{id}", srv.deleteBookHandler).Methods("DELETE")

	srv.srv.Handler = middleware.Logging(middleware.Validating(router))
	return srv
}

func (bs BookServer) ListenAndServer() (<-chan error, error) {
	var err error
	errChan := make(chan error)
	go func() {
		err = bs.srv.ListenAndServe()
		errChan <- err
	}()
	select {
	case err = <-errChan:
		return nil, err
	case <-time.After(time.Second):
		return errChan, nil
	}

}

func (bs *BookServer) Shutdown(ctx context.Context) error {
	return bs.srv.Shutdown(ctx)
}
